package com.kly.controller;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.benmanes.caffeine.cache.Cache;
import com.kly.annotation.GuavaLimitAnno;
import com.kly.annotation.SpiderCheckAnno;
import com.kly.api.ai.AliYunAiApi;
import com.kly.api.ai.model.CreateImageOutPaintingResponse;
import com.kly.api.ai.model.GetImageOutPaintingResponse;
import com.kly.api.imagesearch.model.ImageSearchResult;
import com.kly.api.imagesearch.model.SoImageSearchResult;
import com.kly.api.imagesearch.sub.SoImageSearchApiFacade;
import com.kly.api.imagesearch.subpexels.ImageSearchApiFacade;
import com.kly.common.BaseResponse;
import com.kly.common.DeleteRequest;
import com.kly.common.ResultUtils;
import com.kly.constants.BloomFilterConstant;
import com.kly.constants.RedisConstant;
import com.kly.constants.UserConstant;
import com.kly.domain.dto.pircture.*;
import com.kly.domain.dto.user.TopThumbUserRequest;
import com.kly.domain.entity.Picture;
import com.kly.domain.entity.Space;
import com.kly.domain.entity.User;
import com.kly.domain.enums.ReviewStatusEnum;
import com.kly.domain.vo.PictureCommentVO;
import com.kly.domain.vo.PictureTagCategory;
import com.kly.domain.vo.PictureVO;
import com.kly.domain.vo.UserVO;
import com.kly.exception.BusinessException;
import com.kly.exception.ErrorCode;
import com.kly.exception.ThrowUtils;
import com.kly.manager.CounterManager;
import com.kly.manager.auth.SpaceUserAuthManage;
import com.kly.manager.auth.StpKit;
import com.kly.manager.auth.anno.SaSpaceCheckPermission;
import com.kly.manager.auth.anno.SaSpaceCheckRole;
import com.kly.manager.auth.model.SpaceUserPermissionConstant;
import com.kly.service.PictureCommentService;
import com.kly.service.PictureService;
import com.kly.service.SpaceService;
import com.kly.service.UserService;
import com.kly.utils.BloomFilter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.kly.constants.RedisConstant.*;

/**
 * @author admin
 * @date 2025/3/3
 */
@RestController
@RequestMapping("/picture")
@Slf4j
@RequiredArgsConstructor
public class PictureController {

    private final PictureService pictureService;

    private final UserService userService;

    private final StringRedisTemplate stringRedisTemplate;

    private final SpaceService spaceService;


    private final AliYunAiApi aliYunAiApi;

    private final SpaceUserAuthManage spaceUserAuthManage;

    private final PictureCommentService pictureCommentService;

    private final Cache<String, String> localCache;

    private final BloomFilter bloomFilter;

    private final CounterManager counterManager;


    /**
     * 添加BloomFilter
     */
    @PostMapping("/bloom")
    public BaseResponse<Boolean> addBloomFilter() {
        // 查询所有数据
        List<Picture> pictureList = pictureService.list();
        RBloomFilter<String> filter = bloomFilter.getBloomFilter(BloomFilterConstant.BLOOM_PICTURE_ID);
        stringRedisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            for (Picture picture : pictureList) {
                filter.add(String.valueOf(picture.getId()));
            }
            return null;
        });
        return ResultUtils.success(true);
    }


    /**
     * 保存图片
     *
     * @param file
     * @param pictureUploadRequest
     * @param request
     * @return
     */
    @PostMapping("/save")
    @SaSpaceCheckPermission(SpaceUserPermissionConstant.PICTURE_UPLOAD)
    public BaseResponse<PictureVO> savePicture(
            @RequestPart("file") MultipartFile
                    file,
            PictureUploadRequest pictureUploadRequest,
            HttpServletRequest request
    ) {
        User loginUser = userService.getLoginUser(request);
        PictureVO pictureVO = pictureService.savePicture(file, pictureUploadRequest, loginUser);
        // 添加到bloomFilter
        RBloomFilter<String> filter = bloomFilter.getBloomFilter(BloomFilterConstant.BLOOM_PICTURE_ID);
        filter.add(String.valueOf(pictureVO.getId()));
        // 删除分页缓存
        deletePageCache();
        return ResultUtils.success(pictureVO);
    }


    @PostMapping("/upload/url")
    @SaSpaceCheckPermission(SpaceUserPermissionConstant.PICTURE_UPLOAD)
    public BaseResponse<PictureVO> uploadPictureByUrl(
            @RequestBody PictureUploadRequest pictureUploadRequest,
            HttpServletRequest request
    ) {
        User loginUser = userService.getLoginUser(request);
        String fileUrl = pictureUploadRequest.getFileUrl();
        PictureVO pictureVO = pictureService.savePicture(fileUrl, pictureUploadRequest, loginUser);
        // 添加到bloomFilter
        RBloomFilter<String> filter = bloomFilter.getBloomFilter(BloomFilterConstant.BLOOM_PICTURE_ID);
        filter.add(String.valueOf(pictureVO.getId()));
        // 删除分页缓存
        deletePageCache();
        return ResultUtils.success(pictureVO);
    }


    /**
     * 删除图片
     */
    @PostMapping("/delete")
    @SaSpaceCheckPermission(SpaceUserPermissionConstant.PICTURE_DELETE)
    public BaseResponse<Boolean> deletePicture(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(deleteRequest == null || deleteRequest.getId() <= 0, ErrorCode.PARAMS_ERROR);

        //    校验权限
        User loginUser = userService.getLoginUser(request);

        boolean res = pictureService.deletePicture(deleteRequest.getId(), loginUser);
        ThrowUtils.throwIf(!res, ErrorCode.OPERATION_ERROR, "删除失败");
        // 删除缓存
        String key = PICTURE_DETAIL_KEY + deleteRequest.getId();
        localCache.invalidate(key);
        stringRedisTemplate.delete(key);

        // 删除分页缓存
        deletePageCache();

        return ResultUtils.success(true);
    }

    private void deletePageCache() {
        // 删除分页缓存
        Map<String, Page<PictureVO>> stringPageMap = pictureService.listPictureVOByPage();
        for (Map.Entry<String, Page<PictureVO>> entry : stringPageMap.entrySet()) {
            String pageKey = PICTURE_PAGE_KEY + entry.getKey();
            stringRedisTemplate.delete(pageKey);
        }
    }

    /**
     * 更新图片 管理员
     */
    @PostMapping("/update")
    @SaSpaceCheckRole(UserConstant.ADMIN_ROLE)
    public BaseResponse<Boolean> updatePicture(@RequestBody PictureUpdateRequest pictureUpdateRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(pictureUpdateRequest == null || pictureUpdateRequest.getId() <= 0, ErrorCode.PARAMS_ERROR);
        Long id = pictureUpdateRequest.getId();
        //     查询图片
        Picture pictureDb = pictureService.getById(id);
        ThrowUtils.throwIf(pictureDb == null, ErrorCode.NOT_FOUND_ERROR, "图片不存在");
        //     更新图片
        Picture picture = BeanUtil.copyProperties(pictureUpdateRequest, Picture.class);
        User loginUser = userService.getLoginUser(request);
        pictureService.fillReviewInfo(picture, loginUser);
        //     校验图片信息
        pictureService.validatePictureInfo(picture);
        // 校验权限
        pictureService.checkSpaceAuth(pictureDb, loginUser);

        //     更新图片
        // 删除缓存
        String key = PICTURE_DETAIL_KEY + id;
        localCache.invalidate(key);
        stringRedisTemplate.delete(key);

        // 删除分页缓存
        deletePageCache();
        return ResultUtils.success(pictureService.updateById(picture));
    }

    /**
     * 获取图片 管理员
     */
    @GetMapping("/get")
    @SaSpaceCheckRole(UserConstant.ADMIN_ROLE)
    public BaseResponse<Picture> getPictureById(Long id) {
        ThrowUtils.throwIf(id == null || id <= 0, ErrorCode.PARAMS_ERROR);
        return ResultUtils.success(pictureService.getPictureById(id));
    }


    /**
     * 获取图片 普通用户
     */
    @GetMapping("/get/vo")
    @SpiderCheckAnno
    @GuavaLimitAnno(key = "getPictureVOById", permitsPerSecond = 1)
    public BaseResponse<PictureVO> getPictureVOById(Long id, HttpServletRequest request) {
        ThrowUtils.throwIf(id == null || id <= 0, ErrorCode.PARAMS_ERROR);
        // 爬虫检测
        counterManager.checkSpider();
        String key = PICTURE_DETAIL_KEY + id;
        String pictureCache = localCache.getIfPresent(key);
        // 查询caffeine
        if (StrUtil.isNotBlank(pictureCache)) {
            PictureVO pictureVO = JSONUtil.toBean(pictureCache, PictureVO.class);
            Long spaceId = pictureVO.getSpaceId();
            if (spaceId != null) {
                boolean permission = StpKit.SPACE.hasPermission(SpaceUserPermissionConstant.PICTURE_VIEW);
                ThrowUtils.throwIf(!permission, ErrorCode.NO_AUTH_ERROR, "没有权限");
            }
            return ResultUtils.success(pictureVO);
        }
        // 如果是空值 异常
        ThrowUtils.throwIf("".equals(pictureCache), ErrorCode.NOT_FOUND_ERROR, "图片不存在");
        // 查询redis
        // 查询bloomFilter
        // RBloomFilter<String> filter = bloomFilter.getBloomFilter(BloomFilterConstant.BLOOM_PICTURE_ID);
        // if (!filter.contains(String.valueOf(id))) {
        //     throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "图片不存在");
        // }

        pictureCache = stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isNotBlank(pictureCache)) {
            // 写入caffeine 更新本地缓存
            localCache.put(key, pictureCache);
            // 返回结果
            PictureVO pictureVO = JSONUtil.toBean(pictureCache, PictureVO.class);
            Long spaceId = pictureVO.getSpaceId();
            if (spaceId != null) {
                boolean permission = StpKit.SPACE.hasPermission(SpaceUserPermissionConstant.PICTURE_VIEW);
                ThrowUtils.throwIf(!permission, ErrorCode.NO_AUTH_ERROR, "没有权限");
            }
            return ResultUtils.success(pictureVO);
        }
        ThrowUtils.throwIf("".equals(pictureCache), ErrorCode.NOT_FOUND_ERROR, "图片不存在");
        // 校验权限
        Picture picture = pictureService.getById(id);
        if (picture == null) {
            //     防止缓存穿透 缓存空值
            localCache.put(key, "");
            stringRedisTemplate.opsForValue().set(key, "", PICTURE_DETAIL_TTL, TimeUnit.MINUTES);
            ThrowUtils.throwIf(true, ErrorCode.NOT_FOUND_ERROR, "图片不存在");
        }
        Long spaceId = picture.getSpaceId();
        Space space = null;
        if (spaceId != null) {
            boolean permission = StpKit.SPACE.hasPermission(SpaceUserPermissionConstant.PICTURE_VIEW);
            ThrowUtils.throwIf(!permission, ErrorCode.NO_AUTH_ERROR, "没有权限");
            // 查询数据库
            space = spaceService.getById(spaceId);
            ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在");

        }


        PictureVO pictureVO = pictureService.getPictureVO(picture, request);


        pictureService.setThumb(pictureVO, StpKit.SPACE.getLoginIdAsLong());
        // 设置权限
        User loginUser = userService.getLoginUser(request);
        List<String> permissionList = spaceUserAuthManage.getPermissionList(space, loginUser);
        pictureVO.setPermissionList(permissionList);

        //     写入caffeine 更新本地缓存
        localCache.put(key, JSONUtil.toJsonStr(pictureVO));
        //     写入redis
        int expireTime = PICTURE_DETAIL_TTL + RandomUtil.randomInt(0, 10);
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(pictureVO), expireTime, TimeUnit.MINUTES);


        return ResultUtils.success(pictureVO);
    }


    /**
     * 分页获取图片列表 管理员
     */
    @GetMapping("/list/page")
    @SaSpaceCheckRole(UserConstant.ADMIN_ROLE)
    @GuavaLimitAnno(key = "listPictureByPage")
    public BaseResponse<Page<Picture>> listPictureByPage(PictureQueryRequest pictureQueryRequest) {
        ThrowUtils.throwIf(pictureQueryRequest == null, ErrorCode.PARAMS_ERROR);
        //     获取查询条件
        int current = pictureQueryRequest.getCurrent();
        int pageSize = pictureQueryRequest.getPageSize();
        // 设置查询条件
        pictureQueryRequest.setNullSpaceId(true);
        QueryWrapper<Picture> queryWrapper = pictureService.getQueryWrapper(pictureQueryRequest);
        Page<Picture> page = pictureService.page(new Page<>(current, pageSize), queryWrapper);
        return ResultUtils.success(page);
    }

    /**
     * 分页获取图片列表 普通用户 通过缓存
     */
    @PostMapping("/list/page/vo/cache")
    public BaseResponse<Page<PictureVO>> listPictureVOByPageWithCache(@RequestBody PictureQueryRequest pictureQueryRequest) {
        ThrowUtils.throwIf(pictureQueryRequest == null, ErrorCode.PARAMS_ERROR);
        int current = pictureQueryRequest.getCurrent();
        int pageSize = pictureQueryRequest.getPageSize();
        // 限制爬虫
        ThrowUtils.throwIf(pageSize > 20, ErrorCode.PARAMS_ERROR);
        // 查询缓存
        // 参数MD5
        String jsonStr = JSONUtil.toJsonStr(pictureQueryRequest);
        String token = DigestUtils.md5DigestAsHex(jsonStr.getBytes());

        // 本地缓存
        String key = PICTURE_PAGE_KEY + token;
        String cacheCaffeine = localCache.getIfPresent(key);
        // 设置过期时间 30-35分钟 防止缓存雪崩
        int expireTime = THIRTY_TIME_TTL_LONG + RandomUtil.randomInt(0, 5);
        if (StrUtil.isNotBlank(cacheCaffeine)) {
            Page<PictureVO> page = JSONUtil.toBean(cacheCaffeine, Page.class);
            return ResultUtils.success(page);
        }

        if ("".equals(cacheCaffeine)) {
            //    防止缓存穿透
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "图片不存在");
        }

        // 设置key 通过项目名 + 方法名 + 参数（json）
        // redis缓存
        String cacheRedis = stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isNotBlank(cacheRedis)) {
            Page<PictureVO> page = JSONUtil.toBean(cacheRedis, Page.class);
            // 更新本地缓存
            localCache.put(key, cacheRedis);
            return ResultUtils.success(page);
        }

        if ("".equals(cacheRedis)) {
            //    防止缓存穿透
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "图片不存在");
        }

        // 普通用户只能查询审核通过的图片
        pictureQueryRequest.setReviewStatus(ReviewStatusEnum.PASSED.getValue());
        QueryWrapper<Picture> queryWrapper = pictureService.getQueryWrapper(pictureQueryRequest);

        Page<Picture> picturePage = pictureService.page(new Page<>(current, pageSize), queryWrapper);
        // 判断是否为空
        if (CollUtil.isEmpty(picturePage.getRecords())) {
            // redis
            stringRedisTemplate.opsForValue().set(key, "", expireTime, TimeUnit.MINUTES);
            // caffeine
            localCache.put(key, "");
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "图片不存在");
        }

        Page<PictureVO> pictureVOPage = pictureService.listPictureVOByPage(picturePage);
        // 缓存到redis
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(pictureVOPage), expireTime, TimeUnit.MINUTES);
        // 缓存到caffeine
        localCache.put(key, JSONUtil.toJsonStr(pictureVOPage));
        return ResultUtils.success(pictureVOPage);
    }

    /**
     * 分页获取图片列表 普通用户
     */
    @PostMapping("/list/page/vo")
    public BaseResponse<Page<PictureVO>> listPictureVOByPage(@RequestBody PictureQueryRequest pictureQueryRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(pictureQueryRequest == null, ErrorCode.PARAMS_ERROR);
        int current = pictureQueryRequest.getCurrent();
        int pageSize = pictureQueryRequest.getPageSize();
        // 限制爬虫
        ThrowUtils.throwIf(pageSize > 20, ErrorCode.PARAMS_ERROR);
        // 判断查询内容
        Long spaceId = pictureQueryRequest.getSpaceId();
        if (spaceId == null) {
            // 普通用户只能查询审核通过的图片
            //     公开空间
            pictureQueryRequest.setReviewStatus(ReviewStatusEnum.PASSED.getValue());
            pictureQueryRequest.setNullSpaceId(true);
        } else {
            //     私有图库
            // User loginUser = userService.getLoginUser(request);
            // Space space = spaceService.getById(spaceId);
            // if (!loginUser.getId().equals(space.getUserId())) {
            //     throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "没有权限");
            // }
            boolean permission = StpKit.SPACE.hasPermission(SpaceUserPermissionConstant.PICTURE_VIEW);
            ThrowUtils.throwIf(!permission, ErrorCode.NO_AUTH_ERROR, "没有权限");
        }

        // 查询条件
        QueryWrapper<Picture> queryWrapper = pictureService.getQueryWrapper(pictureQueryRequest);
        // 分页查询
        Page<Picture> picturePage = pictureService.page(new Page<>(current, pageSize), queryWrapper);
        return ResultUtils.success(pictureService.listPictureVOByPage(picturePage));
    }


    /**
     * 编辑图片 普通用户
     */
    @PostMapping("/edit")
    @SaSpaceCheckPermission(SpaceUserPermissionConstant.PICTURE_EDIT)
    public BaseResponse<Boolean> editPicture(@RequestBody PictureEditRequest pictureEditRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(pictureEditRequest == null || pictureEditRequest.getId() <= 0, ErrorCode.PARAMS_ERROR);
        // 1.获取图片信息
        Picture pictureDb = pictureService.getById(pictureEditRequest.getId());
        ThrowUtils.throwIf(pictureDb == null, ErrorCode.NOT_FOUND_ERROR, "图片不存在");
        //     2. 校验图片信息
        Picture picture = BeanUtil.copyProperties(pictureEditRequest, Picture.class);
        picture.setEditTime(new Date());
        //  tags List 转为Json
        picture.setTags(JSONUtil.toJsonStr(pictureEditRequest.getTags()));
        // 补充审核信息
        User loginUser = userService.getLoginUser(request);
        pictureService.fillReviewInfo(picture, loginUser);
        pictureService.validatePictureInfo(picture);

        String key = PICTURE_DETAIL_KEY + pictureEditRequest.getId();
        localCache.invalidate(key);
        stringRedisTemplate.delete(key);

        // 删除分页缓存
        deletePageCache();
        return ResultUtils.success(pictureService.updateById(picture));
    }


    /**
     * 自定义标签
     *
     * @return
     */
    @GetMapping("/tag_category")
    public BaseResponse<PictureTagCategory> listPictureTagCategory() {
        PictureTagCategory pictureTagCategory = pictureService.listPictureTagCategory();
        return ResultUtils.success(pictureTagCategory);
    }


    /**
     * 审核图片
     */
    @PostMapping("/review")
    @SaSpaceCheckRole(UserConstant.ADMIN_ROLE)
    public BaseResponse<Boolean> reviewPicture(@RequestBody PictureReviewRequest pictureReviewRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(pictureReviewRequest == null || pictureReviewRequest.getId() <= 0, ErrorCode.PARAMS_ERROR);
        User loginUser = userService.getLoginUser(request);
        boolean res = pictureService.reviewPicture(pictureReviewRequest, loginUser);
        ThrowUtils.throwIf(!res, ErrorCode.OPERATION_ERROR, "审核失败");
        return ResultUtils.success();
    }


    /**
     * 批量导入图片 管理员
     */
    @PostMapping("/upload/batch")
    @SaSpaceCheckRole(UserConstant.ADMIN_ROLE)
    public BaseResponse<Integer> uploadPictureByBatch(@RequestBody PictureUploadByBatchRequest pictureUploadByBatchRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(pictureUploadByBatchRequest == null, ErrorCode.PARAMS_ERROR);
        User loginUser = userService.getLoginUser(request);
        Integer res = pictureService.uploadPictureByBatch(pictureUploadByBatchRequest, loginUser);
        ThrowUtils.throwIf(res == null, ErrorCode.OPERATION_ERROR, "上传失败");
        return ResultUtils.success(res);
    }

    /**
     * 批量搜索喜欢的图片
     */
    @PostMapping("/like/search")
    @SaCheckLogin(type = StpKit.SPACE_TYPE)
    @GuavaLimitAnno(key = "searchLikePicture", permitsPerSecond = 1)
    public BaseResponse<List<PictureVO>> searchLikePicture(@RequestBody PictureSearchRequest pictureSearchRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(pictureSearchRequest == null, ErrorCode.PARAMS_ERROR);
        User loginUser = userService.getLoginUser(request);
        // 根据查询条件生成key
        String requestJsonStr = JSONUtil.toJsonStr(pictureSearchRequest);
        // md5值
        String token = DigestUtils.md5DigestAsHex(requestJsonStr.getBytes());
        // 查询本地缓存 caffeine
        String key = PICTURE_SEARCH_LIKE_KEY + token;
        String picturesCacheJson = localCache.getIfPresent(key);
        if (StrUtil.isNotBlank(picturesCacheJson)) {
            List<PictureVO> pictureVOList = JSONUtil.toList(picturesCacheJson, PictureVO.class);
            return ResultUtils.success(pictureVOList);
        }
        // 查询redis缓存
        picturesCacheJson = stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isNotBlank(picturesCacheJson)) {
            // 写入本地缓存
            localCache.put(key, picturesCacheJson);
            // 返回数据
            List<PictureVO> pictureVOList = JSONUtil.toList(picturesCacheJson, PictureVO.class);
            return ResultUtils.success(pictureVOList);
        }
        // 查询数据库
        List<String> images = pictureService.searchLikePicture(pictureSearchRequest, loginUser);
        if (CollUtil.isEmpty(images)) {
            //     缓存空值 防止缓存穿透
            localCache.put(key, "");
            stringRedisTemplate.opsForValue().set(key, "");
            return ResultUtils.success(new ArrayList<>());
        }
        List<PictureVO> pictureVOList = new ArrayList<>(images.size());
        images.forEach(item -> {
            PictureVO pictureVO = new PictureVO();
            pictureVO.setUrl(item);
            pictureVOList.add(pictureVO);
        });
        // 写入缓存
        localCache.put(key, JSONUtil.toJsonStr(pictureVOList));
        // 防止缓存雪崩
        int expireTime = PICTURE_SEARCH_LIKE_TTL + RandomUtil.randomInt(0, 5);
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(pictureVOList), expireTime, TimeUnit.MINUTES);
        return ResultUtils.success(pictureVOList);
    }

    /**
     * 以图搜图 360搜索
     */
    @PostMapping("/search/image")
    @GuavaLimitAnno(key = "searchImage", permitsPerSecond = 1)
    public BaseResponse<Page<SoImageSearchResult>> searchImage(@RequestBody SearchPictureByPictureRequest searchPictureByPictureRequest) {
        ThrowUtils.throwIf(searchPictureByPictureRequest == null, ErrorCode.PARAMS_ERROR);
        // 获取图片url
        Long id = searchPictureByPictureRequest.getId();
        Picture pictureDb = pictureService.getById(id);
        ThrowUtils.throwIf(pictureDb == null, ErrorCode.PARAMS_ERROR, "图片不存在");

        Page<SoImageSearchResult> soImageSearchResultPage = new Page<>();
        int start = (searchPictureByPictureRequest.getCurrent() - 1) * 20;
        List<SoImageSearchResult> soImageSearchResults = SoImageSearchApiFacade.searchImage(pictureDb.getOriginalUrl(), start);

        soImageSearchResultPage.setRecords(soImageSearchResults);
        soImageSearchResultPage.setTotal(100);
        return ResultUtils.success(soImageSearchResultPage);
    }

    /**
     * 以图搜图 Pexels搜索
     */
    @PostMapping("/search/image/pexels")
    @GuavaLimitAnno(key = "searchImage", permitsPerSecond = 1)
    public BaseResponse<List<ImageSearchResult>> searchImageByPexels(@RequestBody SearchPictureByPictureRequest searchPictureByPictureRequest) {
        ThrowUtils.throwIf(searchPictureByPictureRequest == null, ErrorCode.PARAMS_ERROR);
        // 获取图片url
        Long id = searchPictureByPictureRequest.getId();
        Picture pictureDb = pictureService.getById(id);
        ThrowUtils.throwIf(pictureDb == null, ErrorCode.PARAMS_ERROR, "图片不存在");

        List<ImageSearchResult> imageSearchResults = ImageSearchApiFacade.searchImage(pictureDb.getName());
        return ResultUtils.success(imageSearchResults);
    }


    /**
     * 通过主色调获取图片
     */
    @PostMapping("/search/color")
    public BaseResponse<List<PictureVO>> searchPictureByColor(@RequestBody SearchPictureByColorRequest searchPictureByColorRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(searchPictureByColorRequest == null, ErrorCode.PARAMS_ERROR);
        // 获取登录用户
        User loginUser = userService.getLoginUser(request);
        List<PictureVO> pictureVOS = pictureService.searchPictureByColor(searchPictureByColorRequest, loginUser);
        return ResultUtils.success(pictureVOS);
    }


    /**
     * 批量编辑图片
     */
    @PostMapping("/edit/batch")
    @SaSpaceCheckPermission(SpaceUserPermissionConstant.PICTURE_EDIT)
    public BaseResponse<Boolean> editPictureByBatch(@RequestBody PictureEditBatchRequest pictureEditBatchRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(pictureEditBatchRequest == null, ErrorCode.PARAMS_ERROR);
        // 获取登录用户
        User loginUser = userService.getLoginUser(request);
        boolean res = pictureService.editPictureBatch(pictureEditBatchRequest, loginUser);
        ThrowUtils.throwIf(!res, ErrorCode.OPERATION_ERROR, "编辑失败");
        return ResultUtils.success(true);
    }


    /**
     * 创建扩图任务请求
     */
    @PostMapping("/outpainting/create_task")
    @SaSpaceCheckPermission(SpaceUserPermissionConstant.PICTURE_EDIT)
    @GuavaLimitAnno(key = "createImageOutPainting", permitsPerSecond = 0.5)
    public BaseResponse<CreateImageOutPaintingResponse> createImageOutPainting(@RequestBody CreatePictureOutPaintingTaskRequest createPictureOutPaintingTaskRequest
            , HttpServletRequest request) {
        ThrowUtils.throwIf(createPictureOutPaintingTaskRequest == null, ErrorCode.PARAMS_ERROR);
        // 获取登录用户
        User loginUser = userService.getLoginUser(request);
        CreateImageOutPaintingResponse createImageOutPaintingResponse = pictureService.createImageOutPainting(createPictureOutPaintingTaskRequest, loginUser);
        return ResultUtils.success(createImageOutPaintingResponse);
    }

    /**
     * 查询扩图任务
     */
    @GetMapping("/outpainting/get_task")
    @SaSpaceCheckPermission(SpaceUserPermissionConstant.PICTURE_VIEW)
    public BaseResponse<GetImageOutPaintingResponse> getImageOutPaintingTask(@RequestParam String taskId, HttpServletRequest request) {
        ThrowUtils.throwIf(taskId == null, ErrorCode.PARAMS_ERROR);
        // 获取登录用户
        User loginUser = userService.getLoginUser(request);
        // 未登录不允许
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR);
        GetImageOutPaintingResponse imageOutPainting = aliYunAiApi.getImageOutPainting(taskId);
        //
        return ResultUtils.success(imageOutPainting);
    }


    /**
     * 点赞
     */
    @PostMapping("/thumb")
    @SaSpaceCheckPermission(SpaceUserPermissionConstant.PICTURE_VIEW)
    @GuavaLimitAnno(key = "doThumbPicture", permitsPerSecond = 1)
    public BaseResponse<Boolean> doThumbPicture(@RequestBody PictureThumbRequest pictureThumbRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(pictureThumbRequest == null, ErrorCode.PARAMS_ERROR);
        User loginUser = userService.getLoginUser(request);
        boolean res = pictureService.doThumbPicture(pictureThumbRequest, loginUser);
        ThrowUtils.throwIf(!res, ErrorCode.OPERATION_ERROR, "点赞失败");

        // 清除缓存
        String key = PICTURE_DETAIL_KEY + pictureThumbRequest.getId();
        localCache.invalidate(key);
        stringRedisTemplate.delete(key);
        return ResultUtils.success(true);
    }


    /**
     * 查询点赞时间前五用户
     */
    @GetMapping("/thumb/top5")
    public BaseResponse<List<UserVO>> getTop5ThumbUserByPictureId(TopThumbUserRequest topThumbUserRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(topThumbUserRequest == null, ErrorCode.PARAMS_ERROR);
        User loginUser = userService.getLoginUser(request);
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR);
        List<User> top5ThumbUserByPictureId = pictureService.getTop5ThumbUserByPictureId(topThumbUserRequest);
        return ResultUtils.success(userService.getUserVoList(top5ThumbUserByPictureId));
    }


    /**
     * 统计页面访问次数
     */
    @GetMapping("/visit/count")
    @GuavaLimitAnno(key = "countPictureVisit", permitsPerSecond = 0.1)
    public BaseResponse<Long> countPictureVisit(@RequestParam Long pictureId) {
        ThrowUtils.throwIf(pictureId == null, ErrorCode.PARAMS_ERROR);
        // 获取用户id
        Long userId = StpKit.SPACE.getLoginIdAsLong();
        // 设置key
        String key = String.format("%s:%s", RedisConstant.PICTURE_VISIT_COUNT_KEY, pictureId);
        stringRedisTemplate.opsForHyperLogLog().add(key, String.valueOf(userId));
        Long count = stringRedisTemplate.opsForHyperLogLog().size(key);
        return ResultUtils.success(count);
    }

    /**
     * 统计页面浏览次数
     */
    @GetMapping("/view/count")
    @GuavaLimitAnno(key = "countPictureView", permitsPerSecond = 0.1)
    public BaseResponse<Long> countPictureView(@RequestParam Long pictureId) {
        ThrowUtils.throwIf(pictureId == null, ErrorCode.PARAMS_ERROR);
        // 获取用户id
        // 设置key
        String key = String.format("%s:%s", RedisConstant.PICTURE_VIEW_COUNT_KEY, pictureId);
        Long increment = stringRedisTemplate.opsForValue().increment(key);
        return ResultUtils.success(increment);
    }

    /**
     * 分页查询评论
     */
    @GetMapping("/comment/page")
    @GuavaLimitAnno(key = "listPictureCommentByPage", permitsPerSecond = 1)
    public BaseResponse<Page<PictureCommentVO>> listPictureCommentByPage(PictureCommentQueryRequest pictureCommentQueryRequest) {
        ThrowUtils.throwIf(pictureCommentQueryRequest == null, ErrorCode.PARAMS_ERROR);
        Page<PictureCommentVO> page = pictureCommentService.listPictureCommentByPage(pictureCommentQueryRequest);
        return ResultUtils.success(page);
    }

    /**
     * 通过网址以图搜图
     */
    @GetMapping("/search/similar/image/url")
    @GuavaLimitAnno(key = "searchSimilarImageByUrl", permitsPerSecond = 0.5)
    public BaseResponse<Page<SoImageSearchResult>> searchSimilarImageByUrl(SearchPictureByPictureRequest searchPictureByPictureRequest) {
        ThrowUtils.throwIf(searchPictureByPictureRequest == null, ErrorCode.PARAMS_ERROR);
        Page<SoImageSearchResult> similarImageByUrl = pictureService.searchSimilarImageByUrl(searchPictureByPictureRequest);
        return ResultUtils.success(similarImageByUrl);
    }




}
